package ptij.ADSL.de.ATUC;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.expr.*;
import ptolemy.data.type.*;
import ptolemy.actor.*;

//////////////////////////////////////////////////////////////////////////
//// InterleaverRx
/**

@author Ivan Jeukens
*/
public class InterleaverRx extends TypedAtomicActor {
    
    public InterleaverRx(CompositeEntity container, String name) throws
            NameDuplicationException, IllegalActionException {
        super(container, name);

	input = new TypedIOPort(this, "input", true, false);
	input.setTypeEquals(BaseType.OBJECT);
	
	output = new TypedIOPort(this, "output", false, true);
	output.setTypeEquals(BaseType.OBJECT);

	LS0Bytes = new Parameter(this, "LS0Bytes", new IntToken(8));
	LS0Map = new Parameter(this, "LS0Map", new BooleanToken(false));
	
	S = new Parameter(this, "S", new IntToken(1));
	
	parityInter = new Parameter(this, "parityInter", new IntToken(2));
	
	D = new Parameter(this, "D", new IntToken(2));
    }

    ///////////////////////////////////////////////////////////////////
    ////                     ports and parameters                  ////
    
    public TypedIOPort input;
    public TypedIOPort output;

    public Parameter LS0Bytes;
    public Parameter LS0Map;
    
    public Parameter S;    
    public Parameter parityInter;
    
    public Parameter D;

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    public void attributeChanged(Attribute attribute)
            throws IllegalActionException {                
        if(attribute == LS0Bytes) {
            IntToken it = (IntToken) LS0Bytes.getToken();
            _LS0Bytes = it.intValue();            
        }
        if(attribute == LS0Map) {
            BooleanToken bt = (BooleanToken) LS0Map.getToken();
	    _LS0Map = bt.booleanValue();   
        }
        else
        if(attribute == S) {
 	    IntToken it = (IntToken) S.getToken();
	    _S = it.intValue();           
        }
        else
        if(attribute == parityInter) {
            IntToken it = (IntToken) parityInter.getToken();
            _nnInter = _kkInter + it.intValue();
        }
        else
        if(attribute == D) {
	    IntToken it = (IntToken) D.getToken();
	    _D = it.intValue();            
        }
	else {
            super.attributeChanged(attribute);
        }        
    }

     public void initialize() throws IllegalActionException {
        super.initialize();	
	
	_kkInter = 1;
	if(_LS0Map) {
	    _kkInter += _LS0Bytes;
	}	
	if(_LS0Map) {
	    _kkInter += 1;
	}
        _kkInter *= _S;			

	if(_nnInter % 2 == 0) {
	    _isEven = true;
      	    _bufSize = _nnInter + 1 + (_D - 1) * (_nnInter + 1);
	}
	else {
	    _isEven = false;
	    _bufSize = _D * _nnInter;
	}

	_buffer = new int[_bufSize];
	_readPos = 0;
	_writePos = 0;
	_iteration = 0;

        _checkParameters();        
    }
    
    public void fire() throws IllegalActionException {
	ObjectToken ot = (ObjectToken) input.get(0);
	int d[] = (int []) ot.getValue();

	if(_isEven) {
	    _writePos = (_writePos + 1) % _bufSize;
	}
        for(int j = 0;j < _nnInter;j++) {
	    _buffer[_writePos] = d[j];
	    _writePos = (_writePos + 1) % _bufSize;
	}
	
	int index = 0;
	if(_isEven) {
	    index = 1;
	}
	if(_iteration ==  _D) {	
            int [] out = new int[_nnInter];
            for(int j = 0;j < _nnInter;j++) {
	        out[j] = _buffer[(_readPos + index + (_D - 1) * index) % _bufSize];
		index++;
	    }
	    
	    if(_isEven) {
                _readPos = (_readPos + _nnInter + 1) % _bufSize;		
	    }
	    else {
                _readPos = (_readPos +  _nnInter) % _bufSize;		
	    }
	    output.send(0, new ObjectToken(out));
	}
        else
	if(_iteration == _D - 1) {
            int [] out = new int[_nnInter];
            for(int j = 0;j < _nnInter;j++) {
	        out[j] = _buffer[(_readPos + index + (_D - 1) * index) % _bufSize];
		index++;
	    }
	    if(_isEven) {
                _readPos = (_readPos + _nnInter + 1) % _bufSize;		
            }
	    else {
                _readPos = (_readPos + _nnInter) % _bufSize;		
	    }
	    output.send(0, new ObjectToken(out));
	    
	    _iteration = _D;
	}
	else {
	    _iteration++;
	}
    }       

    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////
    
    private void _checkParameters() throws IllegalActionException {
	if(_LS0Bytes < 0) {
	    throw new IllegalActionException(this, "LS0 Bytes < 0");
	}
	
	if(_S != 1 && _S != 2 && _S != 4 && _S != 8 && _S != 16) {
	    throw new IllegalActionException(this, "\nInvalid value(" +
	        _S + ") for parameter S");
	}	

	if(_nnInter > 255) {
	    throw new IllegalActionException(this, "Maximum Interleaved codeword"+
	        " size is 255");
	}
        int par = _nnInter - _kkInter;
	if((par < 0) || (par > 16) || (par % 2 != 0)) {
	    throw new IllegalActionException(this, "Invalid number(" + par +
	        ") of interleaved parity bytes");
	}
        if(par % _S != 0) {
	    throw new IllegalActionException(this, "Interleaved parity " +
	        " bytes should be an integer multiple of S!");
	}    

	if((_D != 1) && (_D != 2) && (_D != 4) && (_D != 8) && (_D != 16) &&
	   (_D != 32) && (_D != 64)) {
	    throw new IllegalActionException(this, "Invalid interleaved depth");
	}
    } 

    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////
       
    private int _buffer[];
    private int _bufSize;
    private int _readPos;
    private int _writePos;
    private boolean _isEven;
    private int _iteration;

    private int _LS0Bytes;
    private boolean _LS0Map;
    private int _S, _D;

    private int _nnInter, _kkInter;    
}
